package com.electromagnetic.industry.software.manage.service.serviceimpl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrFormatter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.electromagnetic.industry.software.common.cons.UserConstants;
import com.electromagnetic.industry.software.common.enums.ActiveEnum;
import com.electromagnetic.industry.software.common.enums.EffectFlagEnum;
import com.electromagnetic.industry.software.common.enums.PublishEnum;
import com.electromagnetic.industry.software.common.exception.BizException;
import com.electromagnetic.industry.software.common.pojo.UserLoginInfo;
import com.electromagnetic.industry.software.common.resp.ElectromagneticResult;
import com.electromagnetic.industry.software.common.util.*;
import com.electromagnetic.industry.software.manage.config.ElePropertyConfig;
import com.electromagnetic.industry.software.manage.mapper.RoleMapper;
import com.electromagnetic.industry.software.manage.mapper.TokenMapper;
import com.electromagnetic.industry.software.manage.mapper.UserMapper;
import com.electromagnetic.industry.software.manage.mapper.UserMappers;
import com.electromagnetic.industry.software.manage.pojo.models.Role;
import com.electromagnetic.industry.software.manage.pojo.models.Token;
import com.electromagnetic.industry.software.manage.pojo.models.User;
import com.electromagnetic.industry.software.manage.pojo.models.UserRole;
import com.electromagnetic.industry.software.manage.pojo.other.PublishParam;
import com.electromagnetic.industry.software.manage.pojo.other.SearchKeyWords;
import com.electromagnetic.industry.software.manage.pojo.other.SingleUserResponse;
import com.electromagnetic.industry.software.manage.pojo.req.*;
import com.electromagnetic.industry.software.manage.pojo.resp.UserLoginResponse;
import com.electromagnetic.industry.software.manage.pojo.resp.UserSearchResponse;
import com.electromagnetic.industry.software.manage.service.EdFileFavoriteService;
import com.electromagnetic.industry.software.manage.service.UserRoleService;
import com.electromagnetic.industry.software.manage.service.UserService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.SecretKey;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.date.DateTime.now;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private TokenMapper tokenMapper;
    @Resource
    private UserRoleService userRoleService;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private EdFileFavoriteService edFileFavoriteService;
    @Resource
    private ElePropertyConfig elePropertyConfig;

    /**
     * 用户登录
     *
     * @param loginRequest
     * @return
     */
    @Override
    public ElectromagneticResult<?> login(UserLoginRequest loginRequest) {
        UserLoginInfo info = UserMappers.INSTANCE.getUserLoginRequestToModel(loginRequest);
        String decodePwd = AESUtils.decrypt(info.getUserPwd(), elePropertyConfig.getLoginEncPasswd());
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getWorkNumber, info.getWorkNumber())
                .eq(User::getEffectFlag, EffectFlagEnum.EFFECT.code);
        User user = this.getOne(queryWrapper);
        if (Objects.isNull(user)) {
            return ElectromagneticResultUtil.fail("500", "用户不存在/密码错误");
        }
        info.setUserId(user.getId());
        info.setUsername(user.getUserName());
        info.setAdminType(user.getAdminType());
        if (checkUserValid(user) && matchPassword(user, decodePwd)) {
            String tokenStr = createToken(info);
            createUserToken(user, tokenStr);
            UserLoginResponse userLoginResponse = new UserLoginResponse();
            userLoginResponse.setToken(tokenStr);
            userLoginResponse.setUserId(user.getId());
            UserThreadLocal.setSuccessInfo("", user.getId(), StrFormatter.format("用户 {} 登陆", user.getUserName()));
            return ElectromagneticResultUtil.success(userLoginResponse);
        }
        return ElectromagneticResultUtil.fail("500", "用户不存在/密码错误");
    }

    public Boolean createUserToken(User user, String tokenStr) {
        Token token = new Token();
        token.setUserId(user.getId());
        token.setToken(tokenStr);
        token.setExpireAt(new DateTime(SystemClock.now() + UserConstants.DEFAULT_EXPIRE_TIME));
        return tokenMapper.insert(token) > 0;
    }

    public Boolean checkUserValid(User user) {
        if (user == null
                || Objects.equals(user.getIsPublished(), PublishEnum.UNPUBLISHED.getCode())
                || Objects.equals(user.getIsActivated(), ActiveEnum.UNACTIVATED.getCode())
                || Objects.equals(user.getEffectFlag(), EffectFlagEnum.NOT_EFFECTIVE.code)
        ) {
            return false;
        }
        return user.getInternshipEndDate() == null || !user.getInternshipEndDate().before(now());
    }

    public String createToken(UserLoginInfo loginInfo) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(UserConstants.LOGIN_USER_ID, loginInfo.getUserId());
        claims.put(UserConstants.LOGIN_USER_NAME, loginInfo.getUsername());
        claims.put(UserConstants.LOGIN_WORK_NUMBER, loginInfo.getWorkNumber());
        claims.put(UserConstants.LOGIN_ADMIN_TYPE, loginInfo.getAdminType());
        byte[] keyBytes = elePropertyConfig.getLoginEncPasswd().getBytes(Charset.defaultCharset());
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);
        return Jwts.builder()
                .claims(claims) // 自定义 claims
                .id(UUID.randomUUID().toString()) // JWT ID
                .issuedAt(new Date()) // 签发时间，使用 java.util.Date
                .signWith(key) // 签名
                .compact();
    }

    public Boolean matchPassword(User user, String password) {
        String salt = user.getSalt();
        String encodePwd = EleCommonUtil.getStrMd5(password + salt);
        return user.getUserPwd().equals(encodePwd);
    }

    /**
     * 新增用户信息
     *
     * @param userRequest
     * @return
     */
    @Override
    public ElectromagneticResult<?> createUser(UserRequest userRequest) {

        Assert.isTrue(!userRequest.getWorkNumber().startsWith("1"), "工号不能从1开始，请重新设置。");
        Assert.notNull(userRequest, "用户创建请求参数不能为空");
        User user = UserMappers.INSTANCE.getUserRequestToModel(userRequest);
        user.newInit();
        // 暂时将账户设置为工号
        user.setUserAccount(user.getWorkNumber());
        boolean success = this.save(user);
        UserThreadLocal.setSuccessInfo("", user.getId(), StrFormatter.format("新增了用户 {} ", user.getUserName()));
        return ElectromagneticResultUtil.success(success);
    }

    /**
     * 修改用户信息
     *
     * @param userModiRequest
     * @return
     */
    @Override
    public ElectromagneticResult<?> modifyUser(UserModiRequest userModiRequest) {
        Assert.isTrue(!userModiRequest.getWorkNumber().startsWith("1"), "工号不能从1开始，请重新设置。");
        Assert.notNull(userModiRequest, "用户修改请求参数不能为空");
        String userId = userModiRequest.getId();
        Assert.notBlank(userId, "用户ID不能为空");

        // 校验用户存在性
        User existingUser = this.getById(userId);
        Assert.notNull(existingUser, StrFormatter.format("用户ID {} 不存在", userId));

        User user = new User();
        BeanUtils.copyProperties(userModiRequest, user);
        boolean success = this.updateById(user);
        UserThreadLocal.setSuccessInfo("", userModiRequest.getUserId(), StrFormatter.format("更新了用户 {} ", user.getUserName()));
        return ElectromagneticResultUtil.success(success);
    }

    /**
     * 发布用户信息
     *
     * @param userPublishRequest
     * @return
     */
    @Override
    public ElectromagneticResult<?> publishUser(UserPublishRequest userPublishRequest) {
        Assert.notNull(userPublishRequest, "发布请求参数不能为空");
        PublishParam model = UserMappers.INSTANCE.getUserPublishRequestToModel(userPublishRequest);
        Assert.notEmpty(model.getUserIds(), "待发布用户ID列表不能为空");
        boolean updateResult = this.update(
                new LambdaUpdateWrapper<User>()
                        .in(User::getId, model.getUserIds())
                        .set(User::getIsPublished, PublishEnum.PUBLISHED.getCode())
        );
        UserThreadLocal.setSuccessInfo("", "", StrFormatter.format("批量发布 {} 个用户", model.getUserIds().size()));
        return ElectromagneticResultUtil.success(updateResult);
    }

    @Override
    public ElectromagneticResult<?> validateWorkNum(UserWorkNumRequest request) {

        String workNumber = request.getWorkNumber();
        String currentUserId = request.getUserId();

        boolean exists = lambdaQuery()
                .eq(User::getWorkNumber, workNumber)
                .eq(User::getEffectFlag, EffectFlagEnum.EFFECT.code)
                .exists(); // 判断工号是否存在

        // 新增 直接返回唯一性结果
        if (StringUtils.isBlank(currentUserId)) {
            return ElectromagneticResultUtil.success(!exists);
        }

        // 编辑 需要排除当前用户
        if (exists) {
            User existingUser = lambdaQuery()
                    .eq(User::getWorkNumber, workNumber)
                    .one();
            return ElectromagneticResultUtil.success(existingUser.getId().equals(currentUserId));
        }

        return ElectromagneticResultUtil.success(true);
    }

    @Override
    public ElectromagneticResult<?> getSingleUser(GetSingleUserRequest getSingleUserRequest) {
        User user = userMapper.getSingleUser(getSingleUserRequest.getUserId());
        SingleUserResponse singleUserResponse = UserMappers.INSTANCE.getSingleUserToResponse(user);
        UserThreadLocal.setSuccessInfo("", getSingleUserRequest.getUserId(), StrFormatter.format("查询了用户 {} 详情信息", singleUserResponse.getUserName()));
        return ElectromagneticResultUtil.success(singleUserResponse);
    }

    @Override
    public ElectromagneticResult<?> searchUser(SearchUserRequest searchUserRequest) {
        searchUserRequest.setPageIndex((searchUserRequest.getPageIndex() - 1) * searchUserRequest.getPageSize());
        SearchKeyWords model = new SearchKeyWords();
        BeanUtils.copyProperties(searchUserRequest, model);
        List<User> userList = userMapper.search(model);
        int totalCount = userMapper.getTotalCount(model);

        List<SingleUserResponse> list = new ArrayList<>();
        for (User user : userList) {
            SingleUserResponse singleUserResponse = new SingleUserResponse();
            BeanUtils.copyProperties(user, singleUserResponse);
            List<String> roleList = userMapper.selectUserRoles(user.getId());
            String roles = String.join(",", roleList);
            singleUserResponse.setRoles(roles);
            singleUserResponse.setRoleList(roleList);

            // 检查密码是否和初始密码相同
            if (user.getUserPwd().equals(EleCommonUtil.getStrMd5(UserConstants.DEFAULT_PASSWORD + user.getSalt()))) {
                singleUserResponse.setIsDefaultPwd(1);
            } else {
                singleUserResponse.setIsDefaultPwd(0);
            }

            list.add(singleUserResponse);
        }
        UserSearchResponse userSearchResponse = new UserSearchResponse();
        userSearchResponse.setUserList(list);
        userSearchResponse.setTotalCount(totalCount);
        UserThreadLocal.setSuccessInfo("", "", StrFormatter.format("查询了用户列表"));
        return ElectromagneticResultUtil.success(userSearchResponse);
    }

    @Override
    @Transactional
    public ElectromagneticResult<?> deleteUser(UserDeleteRequest request) {
        String userId = request.getUserId();

        // 校验用户存在性及有效性
        User user = this.getById(userId);
        Assert.notNull(user, StrFormatter.format("用户ID {} 无效", userId));
        Assert.isTrue(user.getEffectFlag().equals(EffectFlagEnum.EFFECT.code),
                StrFormatter.format("用户 {} 已被删除", user.getUserName()));

        // 构建逻辑删除更新
        boolean success = this.update(new LambdaUpdateWrapper<User>()
                .eq(User::getId, userId)
                .set(User::getEffectFlag, EffectFlagEnum.NOT_EFFECTIVE.code));

        if (success) {
            // 级联失效用户角色关系
            userRoleService.logicRemove(userId, null);
            // 级联失效用户文件收藏关系
            edFileFavoriteService.logicRemove(userId, null);
        }

        UserThreadLocal.setSuccessInfo("", "", StrFormatter.format("删除了用户 {} ", user.getUserName()));
        return ElectromagneticResultUtil.success(success);
    }

    @Override
    public ElectromagneticResult<?> logout(String token) {
        token = token.substring(7);
        UserThreadLocal.setSuccessInfo("", "", StrFormatter.format("用户{}登出", UserThreadLocal.getUsername()));
        return ElectromagneticResultUtil.success(tokenMapper.deleteToken(token));
    }

    /**
     * 人员绑定
     */
    @Override
    public Boolean bindRoles(List<UserBindRoleDTO> list) {
        if (list == null || list.isEmpty()) {
            throw new BizException("请勿发布空数据");
        }
        for (UserBindRoleDTO dto : list) {
            activeRole(dto);
            bindRole(dto);
        }
        UserThreadLocal.setSuccessInfo("", "", StrFormatter.format("绑定了人员和角色"));
        return Boolean.TRUE;
    }

    /**
     * 修改用户密码
     *
     * @param userId      用户ID
     * @param newPassword 新密码
     * @return 修改结果
     */
    @Override
    public ElectromagneticResult<?> changePassword(String userId, String newPassword) {
        User user = this.getById(userId);
        Assert.notNull(user, StrFormatter.format("用户ID {} 无效", userId));
        String decodeNewPwd = AESUtils.decrypt(newPassword, elePropertyConfig.getLoginEncPasswd());

        Assert.isTrue(!decodeNewPwd.contains(user.getUserAccount()), "密码最小长度为8，必须至少包含一个大写字母、一个小写字母、一个特殊字符、数字、不可包含账号");
        Assert.isTrue(EleCommonUtil.isPwdValid(decodeNewPwd), "密码最小长度为8，必须至少包含一个大写字母、一个小写字母、一个特殊字符、数字、不可包含账号");
        user.setUserPwd(EleCommonUtil.getStrMd5(decodeNewPwd + user.getSalt()));
        boolean success = this.updateById(user);
        UserThreadLocal.setSuccessInfo("", userId, StrFormatter.format("修改了用户密码"));
        return ElectromagneticResultUtil.success(success);
    }

    /**
     * 重置密码
     *
     * @param userId
     */
    @Override
    @Transactional
    public Boolean resetPassword(String userId) {
        User user = this.getById(userId);
        Assert.notNull(user, StrFormatter.format("用户ID {} 无效", userId));

        user.setUserPwd(EleCommonUtil.getStrMd5(UserConstants.DEFAULT_PASSWORD + user.getSalt()));
        boolean success = this.updateById(user);

        if (success) {
            UserThreadLocal.setSuccessInfo("", userId, StrFormatter.format("重置了用户{}的密码", user.getUserName()));
        }
        return success;
    }

    private void activeRole(UserBindRoleDTO dto) {
        User user = this.getById(dto.getUserId());
        if (user != null && ActiveEnum.UNACTIVATED.getCode().equals(user.getIsActivated())) {
            user.setIsActivated(ActiveEnum.ACTIVATED.getCode());
            this.updateById(user);
        }
    }

    private void bindRole(UserBindRoleDTO dto) {
        String userId = dto.getUserId();
        List<String> roleNames = Optional.ofNullable(dto.getRoleList()).orElse(Collections.emptyList());
        // 删除当前用户原绑定角色
        userRoleService.update(new LambdaUpdateWrapper<UserRole>()
                .eq(UserRole::getUserId, userId)
                .set(UserRole::getEffectFlag, EffectFlagEnum.NOT_EFFECTIVE.code));

        if (!roleNames.isEmpty()) {
            // 2. 批量获取有效角色ID
            Map<String, String> roleIdMap = roleMapper.selectList(
                    new LambdaQueryWrapper<Role>()
                            .in(Role::getRoleName, roleNames)
                            .select(Role::getId, Role::getRoleName)
            ).stream().collect(Collectors.toMap(Role::getRoleName, Role::getId));

            // 3. 处理需要绑定的角色
            List<UserRole> toSaveRoles = new ArrayList<>();
            roleNames.stream()
                    .filter(roleIdMap::containsKey)
                    .forEach(roleName -> {
                        String roleId = roleIdMap.get(roleName);

                        // 先尝试恢复已存在的记录
                        UserRole existing = userRoleService.getOne(new LambdaQueryWrapper<UserRole>()
                                .eq(UserRole::getUserId, userId)
                                .eq(UserRole::getRoleId, roleId)
                                .last("LIMIT 1"));

                        if (existing != null) {
                            // 恢复原有记录
                            userRoleService.update(new LambdaUpdateWrapper<UserRole>()
                                    .eq(UserRole::getId, existing.getId())
                                    .set(UserRole::getEffectFlag, EffectFlagEnum.EFFECT.code));
                        } else {
                            // 创建新记录
                            UserRole newRole = new UserRole();
                            newRole.setUserId(userId);
                            newRole.setRoleId(roleId);
                            newRole.newInit(); // 调用初始化方法设置主键和审计字段
                            toSaveRoles.add(newRole);
                        }
                    });

            if (!toSaveRoles.isEmpty()) {
                userRoleService.saveBatch(toSaveRoles);
            }
        }
    }

    @Override
    public ElectromagneticResult<?> validateOldPassword(String userId, String oldInputPassword) {
        // 使用Service层方法获取用户信息
        User user = this.getById(userId);
        Assert.notNull(user, StrFormatter.format("用户不存在，ID为 {}", userId));

        // 解密并验证密码
        String decodeOldPwd = AESUtils.decrypt(oldInputPassword, elePropertyConfig.getLoginEncPasswd());
        Boolean isValid = matchPassword(user, decodeOldPwd);
        UserThreadLocal.setSuccessInfo("", "", "校验原始密码，当前密码与原始密码匹配结果 {}", isValid ? "通过" : "不通过");
        return ElectromagneticResultUtil.success(isValid);
    }
}
